Imports System.Security.Principal

Public Class Impersonation

    '*********************************************************************************************************************
    '
    '             Class:  Impersonation
    '      Initial Date:  03/21/2007
    '      Last Updated:  09/21/2007
    '     Programmer(s):  Blake Pell (bpell@indiana.edu, blakepell@hotmail.com)    
    '       Description:  This class uses the Windows API's to allow certain segements of code to run
    '                     as a different user.  An example usage of this would be having an ASP.NET
    '                     web-site or a VB.NET windows application connect to a SQL Server with a
    '                     trusted connection other than the person logged into the machine (e.g. a
    '                     proxy account that may have access with a domain account when the user 
    '                     doesn't).  This is especially handy when you need a command against a
    '                     network that can't have the local machine account added to it (e.g. write to
    '                     a network drive, etc.).        
    '      Dependencies:  N/A
    '  Additional Notes:  Parts of this code based on Microsoft support article:  http://support.microsoft.com/kb/306158
    '
    '*********************************************************************************************************************

    Private _Username As String = ""
    Private _Domain As String = ""
    Private _Password As String = ""
    Private _IsImpersonating As Boolean = False

    Dim LOGON32_LOGON_INTERACTIVE As Integer = 2
    Dim LOGON32_PROVIDER_DEFAULT As Integer = 0

    Private impersonationContext As WindowsImpersonationContext

    Declare Function LogonUserA Lib "advapi32.dll" (ByVal lpszUsername As String, _
                            ByVal lpszDomain As String, _
                            ByVal lpszPassword As String, _
                            ByVal dwLogonType As Integer, _
                            ByVal dwLogonProvider As Integer, _
                            ByRef phToken As IntPtr) As Integer

    Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
                            ByVal ExistingTokenHandle As IntPtr, _
                            ByVal ImpersonationLevel As Integer, _
                            ByRef DuplicateTokenHandle As IntPtr) As Integer

    Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
    Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long

    Public Sub New(ByVal Username As String, ByVal Password As String, ByVal Domain As String)
        _Username = Username
        _Password = Password
        _Domain = Domain
    End Sub

    Public Sub New()

    End Sub

    Public Sub ImpersonateUser()

        If _IsImpersonating = True Then
            Throw New Exception("You are already impersonating another user.")
            Exit Sub
        End If

        Dim tempWindowsIdentity As WindowsIdentity
        Dim token As IntPtr = IntPtr.Zero
        Dim tokenDuplicate As IntPtr = IntPtr.Zero

        If RevertToSelf() Then
            If LogonUserA(_Username, _Domain, _Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
                If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
                    tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
                    impersonationContext = tempWindowsIdentity.Impersonate()
                    If impersonationContext Is Nothing Then
                        Throw New Exception("The Impersonation Context is null.  The network returned no user for the credentials you supplied (or the network was down).")
                    End If
                End If
            End If
        End If

        Try
            If Not tokenDuplicate.Equals(IntPtr.Zero) Then
                CloseHandle(tokenDuplicate)
            End If
            If Not token.Equals(IntPtr.Zero) Then
                CloseHandle(token)
            End If
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try

        _IsImpersonating = True

    End Sub

    Public Sub UndoImpersonation()

        If impersonationContext IsNot Nothing Then
            Try
                impersonationContext.Undo()
                impersonationContext.Dispose()
            Catch ex As Exception
                ' eat error
            Finally
                _IsImpersonating = False
            End Try
        End If

    End Sub

    Public Function GetCurrentUsername() As String
        Dim CurrentUsername As String = ""

        Try
            CurrentUsername = System.Security.Principal.WindowsIdentity.GetCurrent.Name
        Catch ex As Exception
            CurrentUsername = ""
        End Try

        Return CurrentUsername

    End Function

    Public Property Username() As String
        Get
            Return _Username
        End Get
        Set(ByVal value As String)
            _Username = value
        End Set
    End Property

    Public Property Password() As String
        Get
            Return _Password
        End Get
        Set(ByVal value As String)
            _Password = value
        End Set
    End Property

    Public Property Domain() As String
        Get
            Return _Domain
        End Get
        Set(ByVal value As String)
            _Domain = value
        End Set
    End Property

    Public ReadOnly Property IsImpersonating() As Boolean
        Get
            Return _IsImpersonating
        End Get
    End Property

End Class
